#pragma once

#include "division_engine/canvas/box_constraints.hpp"
#include "division_engine/canvas/components/render_bounds.hpp"
#include "division_engine/canvas/components/renderable_rect.hpp"
#include "division_engine/canvas/components/renderable_text.hpp"
#include "division_engine/canvas/rect.hpp"
#include "division_engine/canvas/rect_drawer.hpp"
#include "division_engine/canvas/render_manager.hpp"
#include "division_engine/canvas/state.hpp"
#include "flecs/addons/cpp/entity.hpp"
#include <stdint.h>

namespace
{
using division_engine::canvas::Rect;
using division_engine::canvas::components::RenderableRect;
using division_engine::canvas::components::RenderableText;
using division_engine::canvas::components::RenderBounds;
}

struct division_c_renderable_text_t
{
    const char* text;
    glm::vec4 color;
    float font_size;
};

// NOLINTBEGIN(cppcoreguidelines-macro-usage)
#define DIVISION_DECLARE_C_GET_COMPONENT(component_type)                                 \
    component_type division_ffi_get_##component_type(                                    \
        division_engine::canvas::State* state, uint64_t entity_id                        \
    )

#define DIVISION_DECLARE_C_SET_COMPONENT(component_type)                                 \
    void division_ffi_set_##component_type(                                              \
        division_engine::canvas::State* state,                                           \
        uint64_t entity_id,                                                              \
        component_type component                                                         \
    )

#define DIVISION_DECLARE_C_GET_SET_COMPONENT(component_type)                             \
    DIVISION_DECLARE_C_GET_COMPONENT(component_type);                                    \
    DIVISION_DECLARE_C_SET_COMPONENT(component_type);

#define DIVISION_IMPL_C_GET_COMPONENT(component_type)                                    \
    DIVISION_DECLARE_C_GET_COMPONENT(component_type)                                     \
    {                                                                                    \
        return *(flecs::entity { state->world, entity_id }.get<component_type>());       \
    }

#define DIVISION_IMPL_C_SET_COMPONENT(component_type)                                    \
    DIVISION_DECLARE_C_SET_COMPONENT(component_type)                                     \
    {                                                                                    \
        flecs::entity { state->world, entity_id }.set(component);                        \
    }

#define DIVISION_IMPL_C_GET_SET_COMPONENT(component_type)                                \
    DIVISION_IMPL_C_GET_COMPONENT(component_type)                                        \
    DIVISION_IMPL_C_SET_COMPONENT(component_type)

extern "C"
{
    uint64_t division_ffi_create_drawable_rect(
        division_engine::canvas::State* state,
        division_engine::canvas::RenderManager* render_manager
    );

    uint64_t division_ffi_create_drawable_text(
        division_engine::canvas::State* state,
        division_engine::canvas::RenderManager* render_manager
    );

    void
    division_ffi_destroy_entity(division_engine::canvas::State* state, uint64_t entity_id);

    DIVISION_IMPL_C_GET_SET_COMPONENT(RenderableRect);
    DIVISION_IMPL_C_GET_SET_COMPONENT(RenderBounds);

    division_c_renderable_text_t division_ffi_get_c_renderable_text(
        division_engine::canvas::State* state,
        uint64_t entity_id
    )
    {
        return *flecs::entity { state->world, entity_id }
                    .get<division_c_renderable_text_t>();
    }

    void division_ffi_set_c_renderable_text(
        division_engine::canvas::State* state,
        uint64_t entity_id,
        division_c_renderable_text_t component
    )
    {
        flecs::entity { state->world, entity_id }.set(RenderableText {
            std::string { component.text },
            component.color,
            component.font_size,
        });
    }

    glm::vec2 division_ffi_get_screen_size(division_engine::canvas::State* state)
    {
        return state->context.get_screen_size();
    }

    glm::vec2 division_ffi_get_mouse_position(division_engine::canvas::State* state)
    {
        return state->context.get_mouse_position();
    }
}
// NOLINTEND(cppcoreguidelines-macro-usage)